#!/usr/bin python
# -*- coding: utf-8 -*-

import sys, os
import traceback
import time
from podpurneFunkce import vytvorSoubor, vymazSoubor, nactiObsahSouboru, vytvorDatabaziFirebird, vytvorRamDisk
from nacteniKonfigurace import cisloAutomatu, adresarDataTelemetrie, adresarDatabazeAutomatu, semaforNactiDexData
from nacteniKonfigurace import semaforPrihlasenTechnik, semaforPrenesDataTelem, db_filename, semaforDvereAutomatuOtevreny
from nacteniKonfigurace import povoleneIButtony, semaforSacekNaMinceJePrilozen, semaforSacekNaMinceJeVyjmut

povoleneIButtony = povoleneIButtony.replace(' ', '')
seznamPovolenychIButtonu = povoleneIButtony.replace('\n', '').split(',')

from tabulky import UdalostiAutomatu, SmsProOdeslani, vytvorTabulky


class Pyowfs(object):
    def __init__(self, 
                 intervalNacitaniTeploty,
                 kritickaTeplota, 
                 intervalKontrolyKritickeTeploty, 
                 intervalProZaznamenavaniTeploty, 
                 tiskTeploty=False, 
                 prenesDataTelem=False):
        
        self.intervalNacitaniTeploty = intervalNacitaniTeploty
        self.kritickaTeplota = kritickaTeplota
        self.intervalKontrolyKritickeTeploty = intervalKontrolyKritickeTeploty        
        self.casPoslednihoZaznamenaniKritickeTeploty = time.time() - intervalKontrolyKritickeTeploty
        self.intervalProZaznamenavaniTeploty = intervalProZaznamenavaniTeploty
        self.casPoslednihoZaznamenaniTeploty = time.time() - intervalProZaznamenavaniTeploty
        self.tiskTeploty = tiskTeploty
        self.prenesDataTelem = prenesDataTelem
        
        self.automatId = 0
        self.uncachedDirName = "/usr/local/1wire/uncached/"
        self.owfsDirName = "/usr/local/1wire/"
        self.memory = None
        self.packedByte = None
        self.packedString = ""
        self.posledniCasNacteniteploty = time.time() - self.intervalNacitaniTeploty
        self.familyList = []
        self.adresaSacku = "not detected"
        vymazSoubor(semaforSacekNaMinceJePrilozen)
        
#        self.tStampPoslednihoUlozeniUdajuButtonu01 = time.time()
#        self.tStampPoslednihoUlozeniUdajuButtonu014 = time.time()                
        self.nactiSbernici()
        
            
    def nactiSbernici(self):
        """
        owfs ma adresarovou strukturu (proto se to jmenuje owfs)
        nacitanid dat z adapteru na ser. porutu spustime napriklad takto (jako root??):
        # /opt/owfs/bin/owfs --passive /dev/ttyS2 -m /usr/local/tridicka/1wire/
        """
        if os.path.exists(self.uncachedDirName):
            self.familyList = []
            for self.f in os.listdir(self.uncachedDirName):
                self.familyList.append(self.f[0:3])
                if self.f.startswith(("01.", "14.", "28.", "2D.")):
                    # "DS2401" iButton ROM; family code = 01h
                    # "DS1971" nebo "DS2430A" iButton 256 bit EEPROM; family code = 14h
                    # "DS18B20" (teplomer) => family code = 28h    
                    # "DS2431P" 1kB 1Wire EEPROM => family code = 2Dh (pouzivame v sacku na penize)
                    self.nactiOwHodnoty()
                    if self.f.startswith(("01.", "14.")):                        
                        print "typ:", self.type
                        print "adresa:", self.address                        
                        if self.address in seznamPovolenychIButtonu and not os.path.exists(semaforPrihlasenTechnik):
                            vytvorSoubor(semaforPrihlasenTechnik, self.address) # semafor zmizi 15 min. po poslednim zavreni dveri
                            UdalostiAutomatu(popisUdalosti=self.address, periferie = "TECHNIK")
                            vytvorSoubor(semaforNactiDexData, self.tStampPlusIdentifikace)
                            if self.prenesDataTelem:
                                # pozadavek Crocodille znel neprenaset data po navsteve technika
                                vytvorSoubor(semaforPrenesDataTelem) # odesilac posle data nejdrive 5 min po vzniku tohoto semaforu
                            self.zahrajPrihlaseniButtonu()                            
                        elif self.address not in seznamPovolenychIButtonu:
                            textUdalosti = "pokus o neautorizovane otevreni automatu, prilozen cip: %s" % self.address
                            print textUdalosti
                            UdalostiAutomatu(popisUdalosti= textUdalosti, periferie = "ERROR")
                            SmsProOdeslani(textZpravy = textUdalosti)
                            self.zahrajPrihlaseniButtonu()  # aby si vetrelec myslel, ze je vse OK                        
                    
                    elif os.path.exists(semaforDvereAutomatuOtevreny): # svindlujeme, 1Wire sbernice na seriovem portu je pomala
                        if self.f.startswith(('2D.')): # "DS2431P" 1kB 1Wire EEPROM => family code = 2Dh (pouzivame v sacku na penize)
                            if not os.path.exists(semaforSacekNaMinceJePrilozen):
                                if os.path.exists(semaforSacekNaMinceJeVyjmut):
                                    vymazSoubor(semaforSacekNaMinceJeVyjmut)
                                vytvorSoubor(semaforSacekNaMinceJePrilozen)
                                self.adresaSacku = self.address
                                textUdalosti = "prilozen:%s" % self.address
                                print textUdalosti
                                UdalostiAutomatu(popisUdalosti=textUdalosti, periferie = "SACEK")
                            
                        
                    elif self.f.startswith(("28.")):  # typ teplomeru DS18B20 => family 28                            
                        if time.time() > self.posledniCasNacteniteploty + self.intervalNacitaniTeploty:                            
                            self.posledniCasNacteniteploty = time.time()
                            self.nactiTeplotu()
            
#            print self.familyList
            if os.path.exists(semaforDvereAutomatuOtevreny): # svindlujeme, 1Wire sbernice na seriovem portu je pomala
                if '2D.' not in self.familyList and os.path.exists(semaforSacekNaMinceJePrilozen):
                    if not os.path.exists(semaforSacekNaMinceJeVyjmut):
                        vytvorSoubor(semaforSacekNaMinceJeVyjmut)                
                    casVyjmutiSacku   = os.stat(semaforSacekNaMinceJeVyjmut).st_mtime
                    now = time.time()
                    print now, casVyjmutiSacku,  now-casVyjmutiSacku
                    if now-casVyjmutiSacku > 5:
                        vymazSoubor(semaforSacekNaMinceJePrilozen)
                        textUdalosti = "vyjmut:%s" % self.adresaSacku                
                        print textUdalosti
                        UdalostiAutomatu(popisUdalosti=textUdalosti, periferie = "SACEK")    
                        
        else:
            print "adresar 1Wire %s neexistuje" % self.uncachedDirName
            
    def zahrajPrihlaseniButtonu(self):
        hudba = "beep -f 493.9 -l 200 -D 20 -n -f 392.0 -l 400 -n -f 493.9 -l 200 -D 20 -n -f 392.0 -l 400 &"
        os.system(hudba)

    def ulozIdentifikaciDoOwEEPROM(self):
        self.owFileName = self.owfsDirName + self.f + "/memory"        
        _cisloAutomatu = nactiObsahSouboru(cisloAutomatu) # delka 8 bajtu, numeric decimal (alfa znaky byly pri vytvoreni ve 1Wire odriznuty)
        print "cisloAutomatu:", _cisloAutomatu, 
        if _cisloAutomatu:
            _cisloAutomatu = self.hexits2bytes(_cisloAutomatu) # 4 bajty po konverzi
        else:
            _cisloAutomatu = "\x00\x00\x00\x00"
        
        _cisloTechnika = nactiObsahSouboru(semaforPrihlasenTechnik) # 16 bajtu, numeric hex (cislo iButtonu) 
        if _cisloTechnika:
            _cisloTechnika = self.hexits2bytes(_cisloTechnika) # 8 bajtu po konverzi
        else:
            _cisloTechnika = "\x00\x00\x00\x00\x00\x00\x00\x00"
        
#        self.casoveRazitko = time.strftime('%Y.%m.%d_%H%M%S')         
#        self.tStampPlusIdentifikace = "%s;%s" % (self.casoveRazitko, self.address)
        
        _parsovaneCasoveRazitko = self.parsujCasoveRazitko(self.casoveRazitko) # 12 bajtu (po vynechani znaku "." a "_" a tisicileti), numeric decimal 
        _parsovaneCasoveRazitko = self.hexits2bytes(_parsovaneCasoveRazitko) # 6 bajtu po konverzi
        # 4 prvni bajty v EEPROm je cAutomatu, 8 bajtu cTechnika, 6 bajtu tStamp (yymmddhhmmss)
        identifikace = _cisloAutomatu + _cisloTechnika + _parsovaneCasoveRazitko
        self.writeToOwEEPROM(identifikace)

    def writeToOwEEPROM(self, identifikace):
        print "identifikace:",  repr(identifikace)
        try:            
            owFile = open(self.owFileName, 'w')            
            owFile.write(identifikace)
            owFile.close()            
            hudba = "beep -f 200 -d 1 -r 5 -n -f 300 -d 10 -r 4 -n -f  400 -d 100 -r 3 &"
            vytvorSoubor(semaforNactiDexData, self.tStampPlusIdentifikace)            
            vytvorSoubor(semaforPrenesDataTelem) # je zabezpeceno ze odesilac posle data nejdrive 5 min po vzniku tohoto semaforu
                                                 # slouzi pro prenos dat na server zakaznika
        except IOError, e:                        
            print e
            print "nemuzeme zapsat, adresar %s nenalezen" % self.owFileName
            hudba = "beep -f 87.31 -l 220 -n -f 87.31 -l 220 -n -f 87.31 -l 220 -n -f 65.41 -l 550 &"
        os.system(hudba)


    def nactiTeplotu(self):        
        self.owFileName = self.owfsDirName + self.f + "/temperature9" # 9 bit resolution
        # v tomto adresari jsou (krome jineho) soubory:
        # address (obsahuje S/N 289D0572020000D7)
        # temperature (Measured temperature with 12 bit resolution.)
        # temperature9 temperature10 temperature11 temperature12  (floating point Measured temperature at 9 to 12 bit resolution)
        # type (v nem je udaj DS18B20)
        # family  (28)
        # id (9D0572020000)        
        #  teplomer DS18S20 adresar 10.xxx/, soubor temperature
        #  teplomer DS18B20 adresar 28.xxx/, soubory temperature, temperature9, temperature10, temperature11, temperature12,
        #  teplomer DS1822  adresar 22.xzz/, soubory temperature, temperature9, temperature10, temperature11, temperature12,        
        _teplota = self.readOwValue()
        if _teplota:            
            _teplota = float(_teplota.strip()) # owfs vraci string dlouhy 12 znaku (napr. "         1.2")        
        else:
            _teplota = 100 # kdyz neni nic nacteno (byla chyba)
        if self.tiskTeploty:
            print "teplota: %s " % _teplota            
        if _teplota > self.kritickaTeplota:
            if time.time() > self.casPoslednihoZaznamenaniKritickeTeploty + self.intervalKontrolyKritickeTeploty:
                self.casPoslednihoZaznamenaniKritickeTeploty = time.time()
                print "ukladam kritickou teplotu"
                UdalostiAutomatu(popisUdalosti = _teplota, periferie = "TEPLOTA")                                
                SmsProOdeslani(textZpravy = "teplota: %0.2f" % _teplota)
        elif time.time() > self.casPoslednihoZaznamenaniTeploty + self.intervalProZaznamenavaniTeploty:
            print "teplota ulozena do databaze"
            self.casPoslednihoZaznamenaniTeploty = time.time()            
            UdalostiAutomatu(popisUdalosti = '%0.2f' % _teplota, periferie = "TEPLOTA")
        self._teplota = _teplota
    
#    def nahlasTeplotu(self):
#        return self._teplota

    def nactiOwHodnoty(self):
        cesta = self.owfsDirName + self.f
        self.owFileName =  cesta + "/type"
        self.type = self.readOwValue() 
        self.owFileName = cesta + "/r_address"
        self.address = self.readOwValue()
        self.casoveRazitko = time.strftime('%Y.%m.%d_%H%M%S')         
        self.tStampPlusIdentifikace = "%s;%s" % (self.casoveRazitko, self.address)
        
    def readOwValue(self):
        try:
            owFile = open(self.owFileName, 'r')
            owValue = owFile.read()
            owFile.close()        
            return owValue
        except IOError:
            print "adresar %s nenalezen" % self.owFileName        
            
    def parsujCasoveRazitko(self, casoveRazitko):
        casoveRazitko = casoveRazitko[2:] # misto roku 2010 zapiseme pouze 10
        casoveRazitko = casoveRazitko.replace(".", "")
        casoveRazitko = casoveRazitko.replace("_", "")
        return casoveRazitko            
    
    def bytes2hexits(self, packedString):
        """
        rozlozi bajt na jednotlive nibbly (hexity) napriklad 0xFF rozlozi na F F        
        nibble corresponds to a single hexadecimal digit (0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F)
        thus, it is often referred to as a "hex digit" or "hexit"
        """
        _hexitString = ""        
        for packedByte in packedString:
            print repr(packedByte)
            _hexitString+= hex(ord(self.packedByte))[2:]
        return _hexitString
#    hexitString = property(bytes2hexits)
            
      
    def hexits2bytes(self, hexStr):
        """
        sklada vzdy dva hexity dohromady do jednoho bajtu, naprikld dva za sbou jdouci hexity FF slozi do 0xFF
        do jednoho bajtu se nam tak vejdou vzdy dva hexity (dve cisla s hodnotou 0 az F)
        napriklad 6B6B61616474657A686B6C6B6A626262 slozi do (ASCII) kkaadtezhklkjbbb                
        """
        bytes = []
        hexStr = ''.join( hexStr.split(" "))
        for i in range(0, len(hexStr), 2):
            bytes.append(chr(int(hexStr[i:i+2], 16 )))
        return ''.join(bytes)
        
            
  
if __name__ == "__main__":
    from vyhodnoceniStavuSpinace import VyhodnoceniZmenyStavu
    vytvorDatabaziFirebird(adresarDatabazeAutomatu, db_filename)
    vytvorRamDisk()
    vytvorTabulky()
    

#TODO: spravne nastavit kritickou teplotu                     
    oneWire = Pyowfs(intervalNacitaniTeploty = 1, 
                     kritickaTeplota = 20, 
                     intervalKontrolyKritickeTeploty = 5, 
                     intervalProZaznamenavaniTeploty = 15*60,  # 15 minut
                     tiskTeploty=True
                     )    
    
    
    while 1:
        try:
            oneWire.nactiSbernici()
        except:
            traceback.print_exc()
        time.sleep(0.1)
